<?
//include_once dirname(__FILE__).'/PATH.inc.php';
//IncludeClass(Base,App::$PATH_LIBPHP);

define('UPFILE_ERR_NOUPLOAD',1) ;					// 文件未上传
define('UPFILE_ERR_EXMAXBYTE_BY_PHP',2) ;			// 超过限制字节数（在 php.ini 中设定）
define('UPFILE_ERR_UNALLOWEXT',3) ;					// 不允许的文件类型
define('UPFILE_ERR_EXMAXBYTE',4) ;					// 超过限制字节数
define('UPFILE_ERR_STOREDIR_NOTEXIST',5) ;			// 存储目录不存在，且 UploadFile::SetAutoCreateStoreDir(false) ;
define('UPFILE_ERR_STOREDIR_UNCREATE',6) ;			// 无法自动创建 存储目录
define('UPFILE_ERR_UNCOPY',7) ;						// 无法将上传文件 由 临时路径拷贝至 存储路径
define('UPFILE_ERR_CANCEL',8) ;						// 上传被取消

class UploadFile
{
	function UploadFile($strAllowExt=_define_by_App_class_,$nMaxByte=_define_by_App_class_,$strStoreDir=_define_by_App_class_)
	{
		// 允许的类型
		if($strAllowExt==_define_by_App_class_)
			$strAllowExt = App::$UPFILE_ALLOWEXT ;
		$this->SetExts($strAllowExt,true) ;
		
		// 不允许的类型
		$this->SetExts(App::$UPFILE_UNALLOWEXT,false) ;

		$this->SetMaxByte($nMaxByte) ;
		$this->SetStoreDir($strStoreDir) ;
	}
	
	## 属性 -----------------
	
	protected $strLastInput ;
	
	protected  $arrStorePath = array() ;				// 存储路径
	
	protected  $arrStoreSubPath = array() ;				// 存储子目录+存储文件名
	
	protected  $arrStoreFileName = array() ;			// 存储文件名
	
	protected $arrErrors = array() ;
	
	protected $nMaxByte ;								// 允许上传的
	
	protected $arrUnallowExt = array() ;				// 不允许的 文件类型
	protected $arrAllowExt = array() ;					// 允许的 文件类型
	
	protected $bKeepOriginalName = false ;				// 保持原名
	
	protected $bOverlayIfExisted = true ;				// 同名覆盖
	
	protected $strStoreDir ;							// 存储目录
	protected $strStoreSubDir ;							// 存储子目录
	
	protected $callbackGenerateNewName = null ;			// 用于产生新文件名的 回调函数
	
	protected $bAutoCreateStoreDir = true ;				// 自动创建存储目录
	
	protected $nRandStrLen = 4 ;
	
	

	## 方法 --------------
	
	public function Upload($InputName)
	{
		$this->strLastInput = $InputName ;

		if( !isset($_FILES[$InputName]) or empty($_FILES[$InputName]['name']) )
		{
			$this->_PutErrorInfo($InputName,"文件“{$InputName}”未上传",UPFILE_ERR_NOUPLOAD) ;
			return false ;
		}
		
		if( empty($_FILES[$InputName]['tmp_name']) )
		{
			$this->_PutErrorInfo($InputName,"获得文件“{$_FILES[$InputName]['name']}”信息，但文件未上传至服务器，可能文件超过服务器的长度限制（参考 php.ini 的相关设定值）",UPFILE_ERR_EXMAXBYTE_BY_PHP) ;
			return false ;			
		}
		
		// 检查类型
		$strExtName = $this->GetOriginalExt($InputName) ;
		if( !$this->IsAllowExt($strExtName) )
		{
			$this->_PutErrorInfo($InputName,"上传的文件类型：“{$strExtName}”受限制",UPFILE_ERR_UNALLOWEXT) ;
			return false ;
		}

		// 检查字节
		$nByte = $this->GetLength($InputName) ;
		if( $this->nMaxByte>0 && $this->nMaxByte<$nByte )
		{
			$this->_PutErrorInfo($InputName,'上传的文件字节长度（'.UploadFile::ReadableFileLen($nByte).'）超过限制（'.UploadFile::ReadableFileLen($this->nMaxByte).'）',UPFILE_ERR_EXMAXBYTE) ;
			return false ;			
		}
		
		// 确定 文件名
		$strStoreName = $this->MakeStoreName($InputName) ;
		$strStorePath = $this->strStoreDir.$strStoreName ;
		$strStoreRealDir = dirname($strStorePath) ;
		if( !is_dir($strStoreRealDir) )
		{
			if(!$this->bAutoCreateStoreDir)
			{
				$this->_PutErrorInfo($InputName,"存储目录不存在：“{$strStoreRealDir}”",UPFILE_ERR_STOREDIR_NOTEXIST) ;
				return false ;
			}
			else if( !mkdir($strStoreRealDir,0777,true) )
			{
				$this->_PutErrorInfo($InputName,"无法创建存储目录：“{$strStoreRealDir}”",UPFILE_ERR_STOREDIR_UNCREATE) ;
				return false ;
			}
		}

		$this->arrStoreSubPath[$InputName] = $strStoreName ;
		$this->arrStorePath[$InputName] = $strStorePath ;
		
		// 移动文件
		$strTempPath = $this->GetTempPath($InputName) ;
		if( !move_uploaded_file($strTempPath,App::CharsetAppToServer($strStorePath)) )
		{
			$this->_PutErrorInfo($InputName,"将上传文件从临时路径：“{$strTempPath}”拷贝至存储路径：“{$strStorePath}”时发生了错误",UPFILE_ERR_UNCOPY) ;
			return false ;			
		}
		
		return $strStoreName ;
	}

	public function CancelUpload($Input=_last_upload_input_)
	{
		if($Input===_last_upload_input_)
			$Input = $this->strLastInput ;

		if( is_file($this->GetStorePath()) )
			unlink($this->GetStorePath()) ;

		unset($this->arrStoreFileName[$Input]) ;
		unset($this->arrStorePath[$Input]) ;
		unset($this->arrStoreSubPath[$Input]) ;

		$this->_PutErrorInfo($InputName,"文件上传被取消",UPFILE_ERR_CANCEL) ;
	}
	
	public function GetStorePath($Input=_last_upload_input_)
	{ return $this->arrStorePath[($Input==_last_upload_input_)?$this->strLastInput:$Input] ; }
	
	public function GetStoreSubPath($Input=_last_upload_input_)
	{ return $this->arrStoreSubPath[($Input==_last_upload_input_)?$this->strLastInput:$Input] ; }
	
	public function GetStoreFileName($Input=_last_upload_input_)
	{ return $this->arrStoreFileName[($Input==_last_upload_input_)?$this->strLastInput:$Input] ; }
	
	
	## -- 新文件名 -- ##
	public function MakeStoreName($Input=_last_upload_input_)
	{
		if($Input==_last_upload_input_)
			$Input = $this->strLastInput ;
		
		// 通过设定的回调函数 得到新文件名
		if($this->callbackGenerateNewName!==null)
			return call_user_func_array($this->callbackGenerateNewName,array(&$this,$Input)) ;
		
		// 保持 原有文件名
		if($this->bKeepOriginalName)
			return $this->strStoreSubDir.$this->GetOriginalName($Input) ;
		
		// 产生随机文件名
		$NewName = time().'.' ;
		$box = array_merge(range('a','z'),range('A','Z'),range(0,9)) ;
		shuffle($box) ;
		$RandLen = $this->nRandStrLen ;
		while( $RandLen-- > 0)
			$NewName.= $box[ array_rand($box) ] ;
		
		$this->arrStoreFileName[$Input] = $NewName.'-'.str_replace('/','-63-',base64_encode($this->GetOriginalName($Input))) ;
		return $this->strStoreSubDir.$this->arrStoreFileName[$Input] ;
	}
	
	public function SetNewNameGenerater($strFunctionName=null,$mixedClassOrOb=null)
	{
		if($mixedClassOrOb===null)
			$this->callbackGenerateNewName = $strFunctionName ;
		else
			$this->callbackGenerateNewName = array($mixedClassOrOb,$strFunctionName) ;
	}
	
	## -- 存储目录 -- ##
	public function SetStoreDir($strStoreDir=_define_by_App_class_)
	{
		$strStoreDir = ($strStoreDir==_define_by_App_class_)? App::$PATH_USERFILE: $strStoreDir ;
		if( !preg_match('|[\\/]$|',$strStoreDir) )
			$strStoreDir.= '/' ;
		$this->strStoreDir = $strStoreDir ;
	}
	public function SetStoreSubDir($strStoreSubDir)
	{
		if( !preg_match('|[\\/]$|',$strStoreSubDir) )
			$strStoreSubDir.= '/' ;
		$this->strStoreSubDir = $strStoreSubDir ;
	}
	public function GetStoreDir()
	{ return $this->strStoreDir ; }
	public function GetStoreSubDir()
	{ return $this->strStoreSubDir ; }
	
	
	## -- 原始文件信息 -- ##
	
	public function GetOriginalName($InputName=_last_upload_input_)
	{ return $_FILES[($InputName==_last_upload_input_)?$this->strLastInput:$InputName]['name'] ; }
	
	public function GetOriginalExt($InputName=_last_upload_input_)
	{ return $this->GetExtName( $this->GetOriginalName($InputName) ) ; }
	
	public function GetOriginalType($InputName=_last_upload_input_)
	{ return $_FILES[($InputName==_last_upload_input_)?$this->strLastInput:$InputName]['type'] ; }
	
	public function GetTempPath($InputName=_last_upload_input_)
	{ return $_FILES[($InputName==_last_upload_input_)?$this->strLastInput:$InputName]['tmp_name'] ; }
	
	public function GetLength($InputName=_last_upload_input_)
	{ return $_FILES[($InputName==_last_upload_input_)?$this->strLastInput:$InputName]['size'] ; }
	
	
	
	## --- 错误信息 --- ##
	
	protected function _PutErrorInfo($InputName,$Msg,$Code)
	{
		$this->arrErrors[$InputName] = array(
			'Msg' => $Msg ,
			'Code' => $Code ,
		) ;
	}	
	public function GetErrorMsg($InputName=_last_upload_input_)
	{ return $this->arrErrors[($InputName==_last_upload_input_)?$this->strLastInput:$InputName]['Msg'] ; }
	
	public function GetErrorCode($InputName=_last_upload_input_)
	{ return $this->arrErrors[($InputName==_last_upload_input_)?$this->strLastInput:$InputName]['Code'] ; }
	
	
	
	## --- 扩展名/文件类型 --- ##
	static function GetExtName($strFileName)
	{
		$arr = explode('.',$strFileName) ;
		$FileExtName = $arr[ count($arr)-1 ] ;
		return strtolower($FileExtName) ;
	}
	public function SetExts($mixedExtName,$bAllow=true)
	{
		if( $bAllow )
			$arr =& $this->arrAllowExt ;
		else
			$arr =& $this->arrUnallowExt ;
		$mixedExtName = (array) $mixedExtName ;
		foreach ($mixedExtName as $strExtName)
			$arr[$strExtName] = UploadFile::GetExtName($strExtName) ;
	}
	public function RemoveExt($strExtName,$bAllow=true)
	{
		$arr =& $bAllow? $this->arrAllowExt: $this->arrUnallowExt ;
		$strExtName = UploadFile::GetExtName($strExtName) ;
		unset($arr[$strExtName]) ;
	}
	public function GetExts($bAllow=true)
	{
		return $bAllow? $this->arrAllowExt: $this->arrUnallowExt ;
	}
	public function IsAllowExt($strExtName)
	{
		$strExtName = UploadFile::GetExtName($strExtName) ;
		if( isset($this->arrUnallowExt[$strExtName]) )
			return false ;
		if( isset($this->arrAllowExt['*']) )
			return true ;
		return isset($this->arrAllowExt[$strExtName]) ;
	}
	
	
	
	## --- 行为 --- ##
	
	public function KeepOriginalName($bKeepOriginalName=true,$bOverlayIfExisted=false)
	{
		$this->bKeepOriginalName = $bKeepOriginalName ;
		$this->bOverlayIfExisted = $bOverlayIfExisted ;
	}
	
	public function SetOverlayIfExisted($bOverlayIfExisted=false)
	{
		$old = $this->bOverlayIfExisted ;
		$this->bOverlayIfExisted = $bOverlayIfExisted ;
		return $old ;		
	}
	
	public function SetAutoCreateStoreDir($bAutoCreateStoreDir=true)
	{
		$old = $this->bAutoCreateStoreDir ;
		$this->bAutoCreateStoreDir = $bAutoCreateStoreDir ;
		return $old ;		
	}
	
	public function SetMaxByte($nMaxByte=_define_by_App_class_)
	{
		$old = $this->nMaxByte ;
		
		if($nMaxByte==_define_by_App_class_)
			$nMaxByte = App::$UPFILE_MAXBYTE ;
		$this->nMaxByte = $nMaxByte ;

		return $old ;
	}
	
	
	## --- ... --- ##
	static function ReadableFileLen($nByte,$nPrecision=2)
	{
		$unit = 'Byte' ;
		if( $nByte>=1024 )
		{
			$nByte/= 1024 ;
			$unit = 'KB' ;
		}
		if( $nByte>=1048576 )
		{
			$nByte/= 1024 ;
			$unit = 'MB' ;
		}
		if( $nByte>=1073741824 )
		{
			$nByte/= 1024 ;
			$unit = 'GB' ;
		}
		
		return round($nByte,$nPrecision).' '.$unit ;
	}
}

?>